#ifndef _PE_H_
#define _PE_H_
#include <vector>
#include <queue>
#include <cstdint>
#include <iostream>
#include "Memory.h"
#include "DRAM.h"
#include "Controller.h"
#include "Statistics.h"

class PE
{

private:

    enum class RegIdx
    {
        VAL_BASE=0,
        COL_BASE,
        EOF_BASE,
        MAX
    };
    uint32_t val_base_addr;
    uint32_t eof_base_addr;
    uint32_t col_base_addr;
    uint32_t writeback_base_addr;
    uint64_t clks;
    uint32_t buffer_size;
    uint32_t num_of_pipeline;
    std::vector<int> acc_addr;
    std::queue<int> writeback_queue;
    std::vector<int> regs;
    std::vector<int> cols;
    bool completed;
    uint32_t round;
public:
    PE():clks(0), buffer_size(256), num_of_pipeline(16),acc_addr(buffer_size, 0), regs(4, 0), completed(false), round(0)
    {
        regs[0]=0;
        regs[1]=4096*8;
        regs[2]=4096*8*2;
        writeback_base_addr=4096*8*4;
    }
    uint32_t get_clks()
    {
        return clks;
    }
    bool is_completed()
    {
        return completed;
    }
    void read_cols(string filename)
    {
        ifstream read_cols_stream(filename);
        string line;
        while(getline(read_cols_stream, line))
            cols.push_back(stoi(line));
        read_cols_stream.close();
    }
    template < typename T>
    void fill_buffer(ramulator::Memory<T, ramulator::Controller>& memory)
    {
        
            bool stall = false, end = false;
            long addr = 0x123456;
            ramulator::Request::Type type = ramulator::Request::Type::READ;

            ramulator::Request req(addr, type);
    
            while (!end || memory.pending_requests())
            {
   
                req.addr = addr;
                req.type = ramulator::Request::Type::READ;
                if(!end)
                    stall = !memory.send(req);
                else
                    memory.set_high_writeq_watermark(0.0f);
                for(int i=0;i<10;i++)
                {
                    memory.tick();
                    Stats::curTick++; // memory clock, global, for Statistics
                }
                clks ++;
            }
     
    }
    void cal();

    template <typename T>
    void write_back(ramulator::Memory<T, ramulator::Controller>& memory)
    {
        for(int i=0;i<1;i++)
            acc_addr[i]=writeback_base_addr+i*8;

        bool stall = false, end = false;
        long addr = 0;
        uint32_t acc_idx=0;
        ramulator::Request::Type type = ramulator::Request::Type::READ;

        ramulator::Request req(addr, type);
    
        while (!end || memory.pending_requests())
        {
            if(!end&&!stall)
            {
                addr=acc_addr[acc_idx++];
                if(acc_idx==2)
                    end=true;
            }
            req.addr = addr;
            req.type = ramulator::Request::Type::WRITE;
            if(!end)
                stall = !memory.send(req);
            else
                memory.set_high_writeq_watermark(0.0f);
            for(int i=0;i<10;i++)
            {
                memory.tick();
                Stats::curTick++; // memory clock, global, for Statistics
            }
            clks ++;
        }
        
        writeback_base_addr+=8;
        round++;
    }
};

#endif

